using DotNetty.Codecs;
using DotNetty.Codecs.Protobuf;
using DotNetty.Handlers.Logging;
using DotNetty.Handlers.Tls;
using DotNetty.Transport.Bootstrapping;
using DotNetty.Transport.Channels;
using DotNetty.Transport.Channels.Sockets;
using Google.Protobuf;
using System;
using System.Net;
using System.Net.Security;
using System.Net.Sockets;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;

namespace Assets.Client.RunTime
{
    public class UdpClient
    {
        private bool isSsl = false;

        private IChannel channel;

        public bool Connected { get; private set; }

        /// <summary>
        /// 初始化
        /// </summary>
        public UdpClient()
        {
            //Set the following properties to optimize performance and GC
            if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("io.netty.leakDetection.level")))
                Environment.SetEnvironmentVariable("io.netty.leakDetection.level", "Disabled");
            if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("io.netty.noPreferDirect")))
                Environment.SetEnvironmentVariable("io.netty.noPreferDirect", "false");
            if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("io.netty.allocator.maxOrder")))
                Environment.SetEnvironmentVariable("io.netty.allocator.maxOrder", "9");
            if (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("io.netty.recycler.ratio")))
                Environment.SetEnvironmentVariable("io.netty.recycler.ratio", "0");
        }

        /// <summary>
        /// 打开连接
        /// </summary>
        /// <param name="host"></param>
        /// <param name="port"></param>
        /// <returns></returns>
        public async Task<IChannel> ConnectAsync(string host, int port)
        {
            var group = new MultithreadEventLoopGroup(1);
            X509Certificate2 cert = null;
            string targetHost = null;
            if (isSsl)
            {
                // cert = new X509Certificate2(Path.Combine(ExampleHelper.ProcessDirectory, "dotnetty.com.pfx"), "password");
                // targetHost = cert.GetNameInfo(X509NameType.DnsName, false);
            }
            try
            {
                var bootstrap = new Bootstrap();
                bootstrap
                    .Group(group)
                    // .Channel<SocketDatagramChannel>()
                    .ChannelFactory(() => new SocketDatagramChannel(AddressFamily.InterNetwork))
                    .Option(ChannelOption.SoBroadcast, true)
                    .Option(ChannelOption.SoRcvbuf, 1024 * 1024 * 5) // 解决udp丢包
                    .Handler(new ActionChannelInitializer<IChannel>(c =>
                    {
                        IChannelPipeline pipeline = c.Pipeline;
                        if (cert != null)
                        {
                            pipeline.AddLast("tls", new TlsHandler(stream => new SslStream(stream, true, (sender, certificate, chain, errors) => true), new ClientTlsSettings(targetHost)));
                        }
                        // pipeline.AddLast(new LoggingHandler(LogLevel.DEBUG));
                        pipeline.AddLast(new DatagramPacketDecoder(new ProtobufDecoder(Messages.IMessage.Parser)));
                        pipeline.AddLast(new DatagramPacketEncoder<IMessage>(new ProtobufEncoder()));
                        pipeline.AddLast(new UdpClientHandler());
                    }));
                channel = await bootstrap.BindAsync(IPAddress.Parse(host), port).ConfigureAwait(false);
                // channel = await bootstrap.ConnectAsync(IPAddress.Parse(host), 9090).ConfigureAwait(false);  // 单连用这个
                this.Connected = true;
                _ = channel.CloseCompletion.ContinueWith(async (t) =>
                {
                    if (group != null)
                        await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)).ConfigureAwait(false);
                });
                return channel;
            }
            catch (Exception)
            {
                try
                {
                    if (channel != null)
                    {
                        await channel.CloseAsync().ConfigureAwait(false);
                    }
                }
                catch (Exception) { }
                if (group != null)
                {
                    await group.ShutdownGracefullyAsync(TimeSpan.FromMilliseconds(100), TimeSpan.FromSeconds(1)).ConfigureAwait(false);
                } 
                this.Connected = false;
                throw;
            }
        }

        /// <summary>
        /// 断开连接
        /// </summary>
        /// <returns></returns>
        public async Task DisconnectAsync()
        {
            if (this.channel != null)
            {
                await channel.DisconnectAsync().ConfigureAwait(false);
            }
            this.Connected = false;
        }
    }
}
